package com.hbx.auth.jwt;

import com.auth0.jwt.JWT;
import com.auth0.jwt.JWTCreator;
import com.auth0.jwt.JWTVerifier;
import com.auth0.jwt.algorithms.Algorithm;
import com.auth0.jwt.exceptions.JWTCreationException;
import com.auth0.jwt.exceptions.JWTDecodeException;
import com.auth0.jwt.exceptions.JWTVerificationException;
import com.auth0.jwt.exceptions.TokenExpiredException;
import com.auth0.jwt.interfaces.Claim;
import com.auth0.jwt.interfaces.DecodedJWT;
import com.hbx.auth.exception.AuthException;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.util.Date;
import java.util.Map;

/*
 * @Author:hbx
 *
 * */
public class JwtUtil {
    private static final Logger log = LoggerFactory.getLogger(JwtUtil.class);

    /**
     * 生成token
     * @param user
     * @param secret
     * @param expires 单位 s
     * @return
     */
    public static String generateToken(JwtUser user, String secret, Long expires) {
        Date date = new Date(System.currentTimeMillis() + expires * 1000);
        Algorithm algorithm = Algorithm.HMAC256(secret);
        return JWT.create()
                .withClaim("id", user.getId())
                .withClaim("username", user.getUsername())
                .withExpiresAt(date)
                .sign(algorithm);
    }

    /**
     * 生成token
     *
     * @param user
     * @param secret
     * @return
     */
    public static String generateToken(JwtUser user, String secret) {
        Date date = new Date(System.currentTimeMillis() + JwtContents.EXPIRE_TIME * 1000);
        Algorithm algorithm = Algorithm.HMAC256(secret);
        try {
            return JWT.create()
                    .withClaim("id", user.getId())
                    .withClaim("username", user.getUsername())
                    .withExpiresAt(date)
                    .sign(algorithm);
        } catch (IllegalArgumentException e) {
            e.printStackTrace();
        } catch (JWTCreationException e) {
            e.printStackTrace();
        }
        return null;
    }

    /**
     * 刷新token过期时间
     *
     * @param token
     * @param secret
     * @return
     */
    public static String refreshTokenExpired(String token, String secret) {
        DecodedJWT jwt = JWT.decode(token);
        Map<String, Claim> claims = jwt.getClaims();
        try {
            Date date = new Date(System.currentTimeMillis() + JwtContents.EXPIRE_TIME * 1000);
            Algorithm algorithm = Algorithm.HMAC256(secret);
            JWTCreator.Builder builer = JWT.create().withExpiresAt(date);
            for (Map.Entry<String, Claim> entry : claims.entrySet()) {
                // 排除冲突设置的有效时间
                if (!entry.getKey().equals("exp")) {
                    builer.withClaim(entry.getKey(), entry.getValue().as(Object.class).toString());
                }
            }
            return builer.sign(algorithm);
        } catch (JWTCreationException e) {
            log.error("token时间刷新失败,{}", e.getMessage());
            return null;
        }
    }

    /**
     * 校验token是否合法
     *
     * @param token
     * @param user
     * @param secret
     * @return
     */
    public static boolean checkToken(String token, JwtUser user, String secret) {
        try {
            DecodedJWT jwt = JWT.decode(token);
            //根据密钥生成JWT效验器
            Algorithm algorithm = Algorithm.HMAC256(secret);
            JWTVerifier verifier = JWT.require(algorithm)
                    .withClaim("id", user.getId())
                    .withClaim("username", user.getUsername()).build();
            verifier.verify(token);
            return true;
        } catch (TokenExpiredException e1) {
            log.error("令牌已过期");
            throw new AuthException("token令牌已经过期");
        } catch (JWTVerificationException e) {
            log.error("token校验失败,{}", e.getMessage());
            throw new AuthException("token校验失败");
        }
    }

    /**
     * 获得token中的自定义信息，无需secret解密也能获得
     */
    public static String getClaimFiled(String token, String filed) {
        try {
            DecodedJWT jwt = JWT.decode(token);
            return jwt.getClaim(filed).as(Object.class).toString();
        } catch (JWTDecodeException e) {
            return null;
        }
    }

    /**
     * 获得jwt载荷中的信息
     *
     * @param token
     * @return
     */
    public static JwtUser getClaimInfo(String token) {
        try {
            DecodedJWT jwt = JWT.decode(token);
            Map<String, Claim> claims = jwt.getClaims();
            Integer userId = claims.get("id").asInt();
            String username = claims.get("username").asString();
            return new JwtUser(userId, username);
        } catch (JWTDecodeException e) {
            e.printStackTrace();
        }
        return null;
    }

    /**
     * 判断token是否过期
     *
     * @param token
     * @return
     */
    public static boolean isTokenExpire(String token) {
        DecodedJWT jwt = JWT.decode(token);
        long tokenTime = jwt.getExpiresAt().getTime();
        return tokenTime < System.currentTimeMillis();
    }

    /**
     * 获得token的签发时间
     *
     * @param token
     * @return
     */
    public static Date getIssuedAt(String token) {
        try {
            DecodedJWT jwt = JWT.decode(token);
            return jwt.getIssuedAt();
        } catch (JWTDecodeException e) {
            log.error("token时间获取失败,{}", e.getMessage());
        }
        return null;
    }

    /**
     * 获得token的过期时间
     *
     * @param token
     * @return
     */
    public static Date getExpiresAt(String token) {
        try {
            DecodedJWT jwt = JWT.decode(token);
            return jwt.getExpiresAt();
        } catch (JWTDecodeException e) {
            log.error("token时间获取失败,{}", e.getMessage());
        }
        return null;
    }
}
